// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Logger from '../Logger';

export class Events {
    eventsQueue = {}
    target = {}
    disallowRepeat = false
    constructor (target) {
        this.target = target
    }

    add(types = [], fn, once) {
        for (let type of types) {
            this.on(type, fn, once)
        }
    }

    on(type, fn, once) {
        if (this.eventsQueue[type] === undefined) {
            this.eventsQueue[type] = { methods: [], once: false }
        }
        if (typeof fn !== 'function') {
            Logger.error('on', 'the argument is not function.', fn)
            return
        }
        // disallow repeat bind
        if (this.disallowRepeat && (this.eventsQueue[type]['methods'].includes(fn))) {
            return
        }
        (this.eventsQueue[type]).methods.push(fn)
        if (once !== undefined) {
            this.eventsQueue[type].once = once
        }
        return this.target
    }

    once(type, fn) {
        this.on(type, fn, true)
    }
    /**
     * 
     * @param {string} type 
     * @param {Function} [fn] be removed function, default all
     */
    off(type, fn) {
        let event = this.eventsQueue[type];
        if (!event) {
            return
        }
        let methods = event.methods;
        if (!fn) {
            delete this.eventsQueue[type];
        } else {
            let len = methods.length;
            while(len--) {
                if (methods[len] === fn) {
                    methods.splice(len, 1);
                }
            }
        }
        return this.target;
    }

    emit(type, ...args) {
        let event = this.eventsQueue[type];
        if (!event) {
            return
        }
        let methods = event.methods;
        methods.forEach(method => {
            method.apply(this.target, args);
        })
        // remove the event after first called
        if (event.once) {
            this.off(type);
        }
        return this.target;
    }

    clear(type) {
        delete this.eventsQueue[type];
    }

    clearAll() {
        this.eventsQueue = {};
    }
}

export const events = (obj) => {
    return new Events(obj)
}

const getEvents = function(obj) {
    const instance = new Events(obj);
    return (obj) => {
    instance.target = obj;
    return instance;
    }
}

export default getEvents